package database

import (
	"database/sql"
	"errors"
	"fmt"
	"github.com/timandy/routine"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	logger2 "gorm.io/gorm/logger"
	"trpg-common/config"
	"trpg-common/logger"
)

var db *gorm.DB

var dbThreadLocal = routine.NewThreadLocal[*gorm.DB]()

func init() {
	if config.Get("database-url") == nil {
		logger.Logger.Info("service does not need database")
		return
	}
	c, err := gorm.Open(postgres.Open(fmt.Sprintf("%v", config.Get("database-url"))), &gorm.Config{
		Logger: logger2.Default.LogMode(logger2.Info),
	})
	if err != nil {
		panic("failed to connect database, error=" + err.Error())
	}
	db = c
	s, _ := db.DB()
	s.SetMaxIdleConns(25)
	s.SetMaxOpenConns(30)
}

func GetDB() *gorm.DB {
	value := dbThreadLocal.Get()
	if value != nil {
		return value
	}
	return db
}

func StartTrans(level sql.IsolationLevel) {
	begin := db.Begin(&sql.TxOptions{Isolation: level})
	logger.Logger.Debugf("start trans, goid=%d, conn=%v", routine.Goid(), begin)
	dbThreadLocal.Set(begin)
}

func Commit() {
	value := dbThreadLocal.Get()
	if value != nil {
		logger.Logger.Debugf("commit trans, goid=%d, conn=%v", routine.Goid(), value)
		value.Commit()
		dbThreadLocal.Remove()
	} else {
		logger.Logger.Panic("did not start a transaction")
	}
}

func RollBack() {
	value := dbThreadLocal.Get()
	if value != nil {
		logger.Logger.Debugf("rollback trans, goid=%d, conn=%v", routine.Goid(), value)
		value.Rollback()
		dbThreadLocal.Remove()
	} else {
		logger.Logger.Panic("did not start a transaction")
	}
}

func Trans(handle func(), level sql.IsolationLevel) {
	StartTrans(level)
	defer func() {
		err := recover()
		if err != nil {
			RollBack()
			logger.Logger.Panicf("%+v", errors.New(fmt.Sprintf("%+v", err)))
		} else {
			Commit()
		}
	}()
	handle()
}
